import { exec } from 'node:child_process'
import { existsSync, promises as fs } from 'node:fs'
import path, { resolve } from 'node:path'

import { rimraf } from 'rimraf'
import { getAllBlocks } from '@/lib/blocks'
import { registry } from '@/registry/index'
import { ui } from '@/registry/registry-ui'
import { crawlBlock, crawlUI } from './crawl-content'

async function writeFile(path: string, payload: any) {
  return fs.writeFile(
    path,
    `${payload}\n`,
    'utf8',
  )
}

// Generate /registry/new-york-v4/registry-ui.ts
async function buildRegistryUI() {
  const result = await crawlUI(resolve('registry', 'new-york-v4', 'ui'))
  await writeFile(
    resolve('registry', 'registry-ui.ts'),
    `import type { Registry } from "shadcn-vue/schema"

export const ui: Registry["items"] = ${JSON.stringify(result ?? '', null, 2)}`,
  )

  exec(`eslint --fix registry/registry-ui.ts`)
}

// Generate /registry/new-york-v4/registry-blocks.ts
async function buildRegistryBlocks() {
  const result = await crawlBlock(resolve('registry', 'new-york-v4', 'blocks'))
  await writeFile(
    resolve('registry', 'registry-blocks.ts'),
    `import type { Registry } from "shadcn-vue/schema"

export const blocks: Registry["items"] = ${JSON.stringify(result ?? '', null, 2)}`,
  )

  exec(`eslint --fix registry/registry-blocks.ts`)
}

// Generate /registry/new-york-v4/registry-charts.ts
async function buildRegistryCharts() {
  const result = await crawlBlock(resolve('registry', 'new-york-v4', 'charts'))
  await writeFile(
    resolve('registry', 'registry-charts.ts'),
    `import type { Registry } from "shadcn-vue/schema"

export const charts: Registry["items"] = ${JSON.stringify(result ?? '', null, 2)}`,
  )

  exec(`eslint --fix registry/registry-charts.ts`)
}

async function buildRegistryIndex() {
  let index = `/* eslint-disable @typescript-eslint/ban-ts-comment */
/* eslint-disable @typescript-eslint/no-explicit-any */
// @ts-nocheck
// This file is autogenerated by scripts/build-registry.ts
// Do not edit this file directly.

export const Index: Record<string, any> = {`
  for (const item of registry.items) {
    const resolveFiles = item.files?.map(
      file => `registry/new-york-v4/${file.path}`,
    )
    if (!resolveFiles) {
      continue
    }

    const componentPath = item.files?.[0]?.path
      ? `@/registry/new-york-v4/${item.files[0].path}`
      : ''

    index += `
  "${item.name}": {
    name: "${item.name}",
    description: "${item.description ?? ''}",
    type: "${item.type}",
    registryDependencies: ${JSON.stringify(item.registryDependencies)},
    files: [${item.files?.map((file) => {
      const filePath = `registry/new-york-v4/${typeof file === 'string' ? file : file.path}`
      const resolvedFilePath = path.resolve(filePath)
      return typeof file === 'string'
        ? `"${resolvedFilePath}"`
        : `{
      path: "${filePath}",
      type: "${file.type}",
      target: "${file.target ?? ''}"
    }`
    })}],
    categories: ${JSON.stringify(item.categories)},
    meta: ${JSON.stringify(item.meta)},
  },`
  }

  index += `
  }`

  console.log(`#️⃣  ${Object.keys(registry.items).length} items found`)

  // Write style index.
  rimraf.sync(path.join(process.cwd(), 'registry/__index__.ts'))
  await fs.writeFile(path.join(process.cwd(), 'registry/__index__.ts'), index)
}

async function buildRegistryJsonFile() {
  // 1. Fix the path for registry items.
  const fixedRegistry = {
    ...registry,
    items: registry.items.map((item) => {
      const files = item.files?.map((file) => {
        return {
          ...file,
          path: `registry/new-york-v4/${file.path}`,
        }
      })

      return {
        ...item,
        files,
      }
    }),
  }

  // 3. Create the output directory and write registry.json.
  const outputDir = path.join(
    process.cwd(),
    `public/r/styles/new-york-v4`,
  )
  await fs.mkdir(outputDir, { recursive: true })

  const registryJsonPath = path.join(outputDir, 'registry.json')
  await fs.writeFile(registryJsonPath, JSON.stringify(fixedRegistry, null, 2))
  await new Promise<void>((resolve, reject) => {
    exec(`pnpm exec prettier --write "${registryJsonPath}"`, (error) => {
      if (error) {
        reject(error)
      }
      else {
        resolve()
      }
    })
  })

  const tempRegistryPath = path.join(process.cwd(), `registry-new-york-v4.json`)
  await fs.writeFile(tempRegistryPath, JSON.stringify(fixedRegistry, null, 2))
}

async function buildRegistry() {
  return new Promise((resolve, reject) => {
    // Use local shadcn copy.
    const process = exec(
      `node ../../packages/cli/dist/index.js build registry-new-york-v4.json --output public/r/styles/new-york-v4`,
    )

    // exec(
    //   `pnpm dlx shadcn build registry.json --output ../www/src/public/r/styles/new-york-v4`
    // )

    process.on('exit', (code) => {
      if (code === 0) {
        resolve(undefined)
      }
      else {
        reject(new Error(`Process exited with code ${code}`))
      }
    })
  })
}

// shadcn-vue: we dont need to sync as we will be using older version of cli for old registry
// async function syncRegistry() {
//   // Store the current registry content
//   const registryDir = path.join(process.cwd(), 'registry')
//   const registryIndexPath = path.join(registryDir, '__index__.ts')
//   let registryContent = null

//   try {
//     registryContent = await fs.readFile(registryIndexPath, 'utf8')
//   }
//   catch {
//     // File might not exist yet, that's ok
//   }

//   // 0. Copy registries.json from v4 to www before building www registry.
//   const v4RegistriesPath = path.join(process.cwd(), 'public/r/registries.json')
//   const wwwRegistriesPath = path.resolve(
//     process.cwd(),
//     '../www/src/public/r/registries.json',
//   )

//   if (existsSync(v4RegistriesPath)) {
//     // Ensure the www/src/public/r directory exists.
//     await fs.mkdir(path.dirname(wwwRegistriesPath), { recursive: true })
//     // Copy registries.json to www.
//     await fs.cp(v4RegistriesPath, wwwRegistriesPath)
//   }

//   // 1. Call pnpm registry:build for www.
//   await exec('pnpm --filter=www registry:build')

//   // 2. Copy the www/src/public/r directory to v4/public/r.
//   rimraf.sync(path.join(process.cwd(), 'public/r'))
//   await fs.cp(
//     path.resolve(process.cwd(), '../www/src/public/r'),
//     path.resolve(process.cwd(), 'public/r'),
//     { recursive: true },
//   )

//   // 3. Restore the registry content if we had it
//   if (registryContent) {
//     await fs.writeFile(registryIndexPath, registryContent, 'utf8')
//   }
// }

async function buildPublicIndex() {
  rimraf.sync(path.join(process.cwd(), 'public/r/index.json'))
  await fs.writeFile(
    path.join(process.cwd(), 'public/r/index.json'),
    JSON.stringify(ui, null, 2),
  )

  await exec(`eslint --fix public/r/index.json`)
}

async function buildBlocksIndex() {
  const blocks = await getAllBlocks(['registry:block'])

  const payload = blocks.map(block => ({
    name: block.name,
    description: block.description,
    categories: block.categories,
  }))

  rimraf.sync(path.join(process.cwd(), 'registry/__blocks__.json'))
  await fs.writeFile(
    path.join(process.cwd(), 'registry/__blocks__.json'),
    JSON.stringify(payload, null, 2),
  )

  await exec(`eslint --fix registry/__blocks__.json`)
}

try {
  await buildRegistryUI()
  await buildRegistryBlocks()
  await buildRegistryCharts()

  console.log('🗂️ Building registry/__index__.ts...')
  await buildRegistryIndex()

  console.log('🗂️ Building registry/__blocks__.json...')
  await buildBlocksIndex()

  console.log('💅 Building registry.json...')
  await buildRegistryJsonFile()

  console.log('🏗️ Building registry...')
  await buildRegistry()

  console.log('🗂️ Building public/r/index.json...')
  await buildPublicIndex()

  if (existsSync(path.join(process.cwd(), `registry-new-york-v4.json`))) {
    await fs.unlink(path.join(process.cwd(), `registry-new-york-v4.json`))
  }
}
catch (error) {
  console.error(error)
  process.exit(1)
}
